<?php

namespace app\home\controller;

use QL\QueryList;
use app\Model\model\Goods;
use app\Model\model\GoodsSku;

/**
 * 执行采集控制器
 * Class Caiji
 * @package app\home\controller
 */
class Caiji extends Tool
{
    public $category_id; // 绑定本地分类ID
    public $page; // 当前页面
    public $lastPage; // 最大页数
    public $area; // 产品区域(默认国际)

    public function __construct()
    {
        ini_set('max_execution_time', '0');
    }

    public function index($category_id, $page, $area)
    {
        // 初始化信息
        $this->category_id = $category_id;
        $this->page = $page;
        $this->area = $area;
        $this->getList();
//        $ql = new QueryList;
//        $this->getDetail('4614610','',$ql);
    }

    /**
     * 拼接要采集的 url 列表
     * @return string
     */
    protected function listUrl()
    {
        $requestUrl = "http://www.shilladfs.com/estore/kr/zh/ajaxProducts"; //请求地址
        $csrf = "635c3a9e-8306-45af-a1bc-b6c508653a70"; // 请求csrf
        $url = $requestUrl . '?json={"category":20,"sort":"topSelling","size":"30","page":' . $this->page . ',"text":"","within":"","query":"allCategories:2","condition":{"disp2DepthCategoryCode":20,"brand":[],"priceRange":[1,760.5],"skinType":[],"productionType":[],"color":[],"spfIndex":[],"specialPriceYnOption":false,"giftYnOption":false,"isNewProductOption":false,"fastShopYnOption":false,"flatPriceYnOption":false}}&CSRFToken=' . $csrf;
        return $url;
    }

    /**
     * 获取数据列表
     */
    protected function getList()
    {
        echo '开始执行列表采集,本次采集 URL: ' . $this->listUrl() . '<br/>';
        $data = $this->curl($this->listUrl(), 1);
        $data = $this->jsonArray($data); // 格式化数据列表

        if (!isset($data['results'])) {
            echo '无内容数据,需检查 url 或采集已结束!</br>';
            return;
        }
        echo "<br>当前页：" . $data['pagination']['currentPage'] . '<br>当前页数据：10' . '<br>总页数：' . $data['pagination']['numberOfPages'] . '<br>总数据：' . $data['pagination']['totalNumberOfResults'] . '<br/>';
        if ($data['pagination']['currentPage'] >= $data['pagination']['numberOfPages']) {
            echo "<center><h1><font color='red'>全部任务已完成！</font></h1></center>";
            return;
        }
        $urls = [];
        $content = [];
        $skuContent = [];
        foreach ($data['results'] as $v) {
            if (db('goods')->where('goods_id', $v['code'])->find() || db('goods_sku')->where('goods_id', $v['code'])->find() || db('goods_image')->where('goods_id', $v['code'])->find()) {
                // 存在数据
                $this->delGoods($v['code']); // 删除原数据以表示更新
            }
            // 处理商品基本信息
            $content[] = array(
                'goods_id' => $v['code'],
                'goods_name' => $v['name'],
                'brand_name' => $v['brandCategory']['displayName'],
                'category_id' => $this->category_id, // 商品分类id(本地) [[后续注意更改此内容]]
                'spec_type' => 10,
                'goods_price_line_dollar' => $v['userPrice']['salePrice'],
                'goods_price_dollar' => $v['discountPrice'],
                'content' => ' ',
                'goods_no' => $v['refNo'],
                'goods_name_ko' => $this->translation($v['name'])['tgt']
            );

            // 处理商品规格
            $skuContent[] = array(
                'goods_id' => $v['code'],
                'goods_price' => round($v['discountPrice'] * 6.9789), // 转换人民币价格
                'line_price' => round($v['userPrice']['salePrice'] * 6.9789), // 转换人民币价格
                'stock_num' => 100,
                'goods_weight' => 1,
                'wxapp_id' => 10001,
                'create_time' => time(),
                'update_time' => time()
            );
            $urls[$v['code']] = $this->detailUrl('zh', $v['code']);
        }
        $model = new Goods;
        $model->insertData($content); // 批量商品列表数据
        $modelSku = new GoodsSku;
        $modelSku->insertData($skuContent); // 批量添加商品规格数据
        echo "<br>商品列表与规格采集完成,采集列表数:<b><font color='red'>" . count($content) . '</font></b><br/>';
        echo "正在分配线程采集商品详情数据...<br/>";
        // 实例化采集器
        $ql = new QueryList;
        // 执行多线程采集详情
        $this->curl_multi($urls, $ql);
        echo "<script>window.location='?page=" . ($this->page + 1) . "&category_id=" . $this->category_id . "&area=" . $this->area . "'</script>";
    }

    /**
     * 拼接要采集详情页 url
     * @param $type 语言 zh 中文  ko 韩文
     * @param $id
     * @return string
     */
    protected function detailUrl($type, $id)
    {
        return 'https://www.shilladfs.com/estore/kr/' . $type . '/p/' . $id;
    }


    /**
     * 获取商品详情
     * @param $goodsList
     */
    protected function getDetail($id, $html, $ql)
    {
//        $url = $this->detailUrl('zh', $id);
//        $html = $this->curl($url);
        // 判断内页模版
        if ($ql->html($html)->find('.prtit_wrap')->html()) {
            // 进入模板 1(基础模板代表 id:4614610)
            echo '商品 ID:' . $id . '进入模板 1 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 1, $html, $ql);
        }

        if ($ql->html($html)->find('.gnb_depth')->html()) {
            // 进入模板 3(代表 id:4398761 注意此模版与 2 相像)
            echo '商品 ID:' . $id . '进入模板 3 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 3, $html, $ql);
        }
        if ($ql->html($html)->find('#el_wrapper')->html()) {
            // 进入模板 4(代表 id:3714363)
            echo '商品 ID:' . $id . '进入模板 4 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 4, $html, $ql);
        }
        if ($ql->html($html)->find('.prodBtn')->html()) {
            // 进入模板 5(代表 id:4600651)
            echo '商品 ID:' . $id . '进入模板 5 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 5, $html, $ql);
        }
        if ($ql->html($html)->find('.infoTable')->html()) {
            // 进入模板 6(代表 id:4631197)
            echo '商品 ID:' . $id . '进入模板 6 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 6, $html, $ql);
        }
        if ($ql->html($html)->find('.top-ga')->html()) {
            // 进入模板 7(代表 id:3182673)
            echo '商品 ID:' . $id . '进入模板 7 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 7, $html, $ql);
        }
        if ($ql->html($html)->find('.product-navi')->html()) {
            // 进入模板 8(代表 id:377198)
            echo '商品 ID:' . $id . '进入模板 8 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 8, $html, $ql);
        }
        if ($ql->html($html)->find('.diorlogo2')->html()) {
            // 进入模板 9(代表 id:3333756)
            echo '商品 ID:' . $id . '进入模板 9 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 9, $html, $ql);
        }
        if ($ql->html($html)->find('.pSection1')->html()) {
            // 进入模板 10(代表 id:4667094)
            echo '商品 ID:' . $id . '进入模板 10 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 10, $html, $ql);
        }
        if ($ql->html($html)->find('.fresh-wrap')->html()) {
            // 进入模板 11(代表 id:4675550)
            echo '商品 ID:' . $id . '进入模板 11 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 11, $html, $ql);
        }
        if ($ql->html($html)->find('#avd_gnb')->html()) {
            // 进入模板 12(代表 id:2041488)
            echo '商品 ID:' . $id . '进入模板 12 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 12, $html, $ql);
        }
        if ($ql->html($html)->find('#bbheader')->html()) {
            // 进入模板 13(代表 id:4541155)
            echo '商品 ID:' . $id . '进入模板 13 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 13, $html, $ql);
        }
        if ($ql->html($html)->find('.btn_red')->html()) {
            // 进入模板 14(代表 id:72534)
            echo '商品 ID:' . $id . '进入模板 14 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 14, $html, $ql);
        }
        if ($ql->html($html)->find('#cl_container')->html()) {
            // 进入模板 15(代表 id:4639780)
            echo '商品 ID:' . $id . '进入模板 15 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 15, $html, $ql);
        }
        if ($ql->html($html)->find('.sub_txt_tit2')->html()) {
            // 进入模板 16(代表 id:3640164)
            echo '商品 ID:' . $id . '进入模板 16 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 16, $html, $ql);
        }
        if ($ql->html($html)->find('.first_list_info')->html()) {
            // 进入模板 17(代表 id:3368018)
            echo '商品 ID:' . $id . '进入模板 17 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 17, $html, $ql);
        }
        if ($ql->html($html)->find('.house99_wrap')->html()) {
            // 进入模板 18(代表 id:3687078)
            echo '商品 ID:' . $id . '进入模板 18 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 18, $html, $ql);
        }
        if ($ql->html($html)->find('.section_right')->html()) {
            // 进入模板 19(代表 id:4558900)
            echo '商品 ID:' . $id . '进入模板 19 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 19, $html, $ql);
        }
        if ($ql->html($html)->find('.i1')->html()) {
            // 进入模板 2(代表 id:4370370)
            echo '商品 ID:' . $id . '进入模板 2 规则,执行采集中...<br/>';
            return $this->getTemplate($id, 2, $html, $ql);
        }
        $ql->destruct();
        echo '<font color="red">商品 ID:' . $id . '无规则提供,本次异常已记录!</font><br/>';
        $this->log('[' . date('Y-m-d H:i:s') . '] 商品' . $id . '无规则提供!', './upload/goods_info.txt');
    }

    /**
     * 获取具体模板内容
     * @param $id 商品 id
     * @param $type 模板编号
     * @param $html
     * @param $koUrl
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    protected function getTemplate($id, $type, $html, $ql)
    {
        echo '商品 ID:' . $id . '开始采集商品详情数据...<br/>';
        $rules = $this->rules('info' . $type);
        $ruleImg = $this->rules('img' . $type);
        $detail = $ql->html($html)
            ->rules($rules)
            ->query()
            ->getData()
            ->all();
//        var_dump($detail);
//        die;
        echo '商品 ID:' . $id . '采集详情完毕,准备整理数据!<br/>';
        $data = [];
        if ($id != 4310356) {
            $data['goods_sku'] = $detail[0]['goods_sku'];

            if (isset($detail[0]['content'])) {
                $data['content'] = htmlspecialchars($detail[0]['content']);
            } else {
                echo '<font color="red">商品 ID:' . $id . '无详情数据,本次异常已记录!</font><br/>';
                $this->log('[' . date('Y-m-d H:i:s') . '] 商品' . $id . '无详情数据!', './upload/goods_info.txt');
            }
            // 地区
            if ($this->area == 10 && isset($detail[0]['area'])) {
                $data['goods_area'] = $detail[0]['area'] == '日本' ? 30 : 10;
            } else {
                $data['goods_area'] = $this->area;
            }
            if ($type == 1 || $type == 7) {
                // 模板 1 和 7允许 skuinfo 为空
                if (isset($detail[0]['skuinfo'])) {
                    $data['skuinfo'] = $detail[0]['skuinfo'];
                } else {
                    echo '<font color="red">商品 ID:' . $id . '无skuinfo,本次异常已记录!</font><br/>';
                }
            } else {
                $data['skuinfo'] = $detail[0]['skuinfo'];
            }
            $data['sales_initial'] = mt_rand(100, 999);
            $data['delivery_id'] = 10001;
            $data['unit'] = '';
            $data['spec_type'] = 10;
            $data['category_id'] = $this->category_id;
            $data['create_time'] = time();
            $data['update_time'] = time();
        } else {
            $data['unit'] = '';
            $data['spec_type'] = 10;
            $data['sales_initial'] = mt_rand(100, 999);
            $data['category_id'] = $this->category_id;
            $data['create_time'] = time();
            $data['update_time'] = time();
            $data['delivery_id'] = 10001;
            $data['goods_area'] = $this->area;
            $this->log('[' . date('Y-m-d H:i:s') . '] 商品' . $id . '指定略过采集!', './upload/goods_info.txt');
            echo '<font color="red">商品 ID:' . $id . '指定略过采集,本次异常已记录!</font><br/>';
        }
//        var_dump($data);die;
        echo '商品 ID:' . $id . '整理数据完毕,准备采集商品图片...<br/>';
        // 采集图片
        $img = $ql->html($html)
            ->rules($ruleImg)
            ->query()
            ->getData()
            ->all();
        // 释放内存
        $ql->destruct();
//        var_dump($img);die;
        echo '商品 ID:' . $id . '采集图片完毕,准备图片本地化并入库...<br/>';
        foreach ($img as $v) {
            $file_img = $this->downloadImageFromUrl($v['img'], $id);
            // 存入图片数据表
            $image_id = $this->saveImg($file_img['FileName'] . '.' . $file_img['extension'], $file_img['extension']);
            $this->saveGoodsImg($id, $image_id); // 存入商品图片表数据库
        }
        echo '商品 ID:' . $id . '图片本地化入库完毕,准备入库本次商品详情数据...<br/>';
        $model = new Goods;
        $row = $model->saveData($id, $data);
        if (!$row) {
            $this->log('[' . date('Y-m-d H:i:s') . '] 商品' . $id . '详情更新失败!', './upload/goods_info.txt');
            echo '<font color="red">商品 ID:' . $id . '入库失败,本次异常已记录!</font><br/>';
            echo '====================================================================================================<br/>';
            echo '<br/>';
            return false;
        }
        echo '商品 ID:' . $id . '入库成功,本商品采集完成!<br/>';
        echo '====================================================================================================<br/>';
        echo '<br/>';
        return true;
    }


    /**
     * 多线程curl
     * @param array $urls
     * @return array|bool
     */
    function curl_multi($urls, $ql)
    {
        if (count($urls) == 0) {
            return false;
        }
        $curl = $curl2 = $text = array();
        $handle = curl_multi_init();
        function createCh($url)
        {
            $xforip = rand(1, 254) . "." . rand(1, 254) . "." . rand(1, 254) . "." . rand(1, 254);
            $header = array("Connection: Keep-Alive", "Accept: application/json, text/javascript, */*; q=0.01", "Pragma: no-cache", "Accept-Language: zh-Hans-CN,zh-Hans;q=0.8,en-US;q=0.5,en;q=0.3", "User-Agent: Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14", 'CLIENT-IP:' . $xforip, 'X-FORWARDED-FOR:' . $xforip); // 请求头信息
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $url);
            //设置头部
            curl_setopt($ch, CURLOPT_REFERER, $url); //设置来源
            curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
            curl_setopt($ch, CURLOPT_ENCODING, "gzip"); // 编码压缩
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); //是否采集301、302之后的页面
            curl_setopt($ch, CURLOPT_MAXREDIRS, 5); //查找次数，防止查找太深
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // 对认证证书来源的检查
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); // 从证书中检查SSL加密算法是否存在
            curl_setopt($ch, CURLOPT_TIMEOUT, 20);
            curl_setopt($ch, CURLOPT_HEADER, 0); //输出头部
            return $ch;
        }

        //准备分配线程
        foreach ($urls as $k => $v) {
            $url = $urls[$k];
            $curl[$k] = createCh($url);
            //向curl批处理会话中添加单独的curl句柄
            curl_multi_add_handle($handle, $curl[$k]);
        }
        $active = null;
        do {
            //运行当前 cURL 句柄的子连接
            $mrc = curl_multi_exec($handle, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);

        while ($active && $mrc == CURLM_OK) {
            //等待所有cURL批处理中的活动连接
            if (curl_multi_select($handle) != -1) {
                usleep(100);
            }
            do {
                //运行当前 cURL 句柄的子连接
                $mrc = curl_multi_exec($handle, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }

        foreach ($curl as $k => $v) {
            if (curl_error($curl[$k]) == "") {
                //如果没有报错则将获取到的字符串添加到数组中
//                $text[$k] = (string)curl_multi_getcontent($curl[$k]);
                $text[$k] = $this->getDetail($k, (string)curl_multi_getcontent($curl[$k]), $ql);
            }
            //移除并关闭curl该句柄资源
            curl_multi_remove_handle($handle, $curl[$k]);
            curl_close($curl[$k]);
        }
        //关闭cURL句柄
        curl_multi_close($handle);
        //将数组返回
        return $text;
    }

}
